package uk.co.q3c.deplan.server.unit.domain.resource;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.Test;

import uk.co.q3c.deplan.client.domain.resource.BaseCalendar;
import uk.co.q3c.deplan.client.domain.resource.DefaultWorkingTime;
import uk.co.q3c.deplan.client.domain.resource.IndividualResource;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.domain.resource.ResourceGroup;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.server.unit.dao.LocalDatastoreTestCase;

import com.google.appengine.repackaged.com.google.common.collect.Lists;

/**
 * NOTE: Any test for booking time requires resource to be assigned to a task.
 * 
 * @author DSowerby 1 Jan 2009
 * 
 */
@Test
public class Resource_UT extends LocalDatastoreTestCase {
	protected final Logger logger = Logger.getLogger(getClass().getName());
	SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yy hh:mm:ss");
	IndividualResource r1;
	IndividualResource ra;
	IndividualResource rb;
	static DefaultWorkingTime dwt;
	static BaseCalendar baseCalendar;
	static DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
	BaseTask rt;

	@Override
	protected void beforeClass() throws Exception {
		super.beforeClass();
		dwt = new DefaultWorkingTime();

	}

	@Override
	protected void afterClass() throws Exception {
		rt = null;
		super.afterClass();
	}

	@Override
	protected void beforeMethod() throws Exception {
		super.beforeMethod();
		rt = new BaseTask(connection);
		r1 = createIndividualResource("dsowerby");
		connection.openSession();
	}

	@Override
	protected void afterMethod() throws Exception {
		connection.closeSession();
		super.afterMethod();
	}

	private IndividualResource createIndividualResource(String name) {
		IndividualResource resource = new IndividualResource(connection);
		resource.setName(name);
		baseCalendar = new BaseCalendar(connection);
		resource.setBaseCalendar(baseCalendar);
		resource.save();
		return resource;
	}

	private ResourceGroup createResourceGroup() throws PersistenceReferenceException {
		ResourceGroup group = createEmptyResourceGroup();
		ra = createIndividualResource("dsowerby");
		rb = createIndividualResource("mpaine");
		group.addResource(ra);
		group.addResource(rb);
		group.save();
		return group;
	}

	private ResourceGroup createEmptyResourceGroup() {
		ResourceGroup group = new ResourceGroup(connection);
		return group;
	}

	public void effortAvailableAndBookEffort() throws PersistenceReferenceException {
		rt.setAssignedResource(r1);
		// 0 returned for Sat and Sun
		Assert.assertEquals(0, r1.effortAvailable(new GregorianCalendar(2008, 3, 12).getTime(), 1));
		Assert.assertEquals(0, r1.effortAvailable(new GregorianCalendar(2008, 3, 13).getTime(), 0.5));
		//
		// Mon should return 450 at 100%
		Calendar di = new GregorianCalendar(2008, 3, 14);
		Assert.assertEquals(r1.effortAvailable(di.getTime(), 1), 450);
		// Mon should return 225 at 50%
		Assert.assertEquals(r1.effortAvailable(di.getTime(), 0.5), 225);
		// Reduce the day to 350 and 50% should be 175, leaving 175
		r1.getResourceCalendar()
				.addAdjustment(di.getTime(), false, -100);
		Assert.assertEquals(r1.effortAvailable(di.getTime(), 0.5), 175);

		// 50 utilised, therefore only 300 available at 100%
		r1.bookEffort(50, di.getTime(), rt);
		Assert.assertEquals(r1.effortAvailable(di.getTime(), 1), 300);

		r1.bookEffort(500, di.getTime(), rt);
		Assert.assertEquals(r1.effortAvailable(di.getTime(), 1), -200, "over booked");

	}

	public void groupEffortAvailable() throws PersistenceReferenceException {
		ResourceGroup group = createResourceGroup();
		Assert.assertEquals(group.count(), 2);
		Calendar di = new GregorianCalendar(2008, 4, 5);
		Assert.assertEquals(group.effortAvailable(di.getTime(), 1), 900);
		Assert.assertEquals(group.effortAvailable(di.getTime(), 0.5), 450);
		IndividualResource nra = group.getResource(ra.getKey());
		nra.getResourceCalendar()
				.addAdjustment(2008, 4, 5, true, 100);
		Assert.assertEquals(group.effortAvailable(di.getTime(), 1), 550);
	}

	public void groupAdjustment() throws PersistenceReferenceException {
		ResourceGroup group = createResourceGroup();
		group.save();
		Calendar di = new GregorianCalendar(2008, 4, 5);
		group.addAdjustment(di.getTime(), true, 15);
		Assert.assertEquals(group.getResource(ra.getKey())
				.effortAvailable(di.getTime(), 1), 15);
		Assert.assertEquals(group.getResource(rb.getKey())
				.effortAvailable(di.getTime(), 1), 15);
	}

	public void groupBookEffortEqualDistribution() throws PersistenceReferenceException {
		ResourceGroup group = createResourceGroup();
		rt.setAssignedResource(group);
		Calendar di = new GregorianCalendar(2008, 4, 5);
		// ra will have 150 available, rb 450
		ra.addAdjustment(di.getTime(), true, 150);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 150);

		group.setProportionalBooking(false);
		group.bookEffort(200, di.getTime(), rt);

		// Should take 100 from each
		Assert.assertEquals(ra.timeBooked(di.getTime()), 100);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 100);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 50);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 350);
		// now take another 200 - ra doesn't have enough, difference shold be
		// borne
		// by rb

		group.bookEffort(200, di.getTime(), rt);
		Assert.assertEquals(ra.timeBooked(di.getTime()), 150);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 250);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 0);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 200);

		group.bookEffort(200, di.getTime(), rt);
		Assert.assertEquals(ra.timeBooked(di.getTime()), 150);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 450);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 0);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 0);

		// over booking ignored
		group.bookEffort(200, di.getTime(), rt);
		Assert.assertEquals(ra.timeBooked(di.getTime()), 150);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 450);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 0);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 0);
		Assert.assertEquals(group.timeBooked(di.getTime()), 600);
	}

	public void groupBookEffortProportionalDistribution() throws PersistenceReferenceException {
		ResourceGroup group = createResourceGroup();
		rt.setAssignedResource(group);
		rt.save();
		Calendar di = new GregorianCalendar(2008, 4, 5);
		// ra will have 150 available, rb 450
		ra.addAdjustment(di.getTime(), true, 150);
		ra.save();
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 150);

		group.setProportionalBooking(true);
		group.bookEffort(200, di.getTime(), rt);
		rt.save();

		// Should take on 3:1 ratio
		Assert.assertEquals(ra.timeBooked(di.getTime()), 50);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 150);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 100);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 300);
		// now take another 200 - ra doesn't have enough, difference should be
		// borne by rb

		group.bookEffort(200, di.getTime(), rt);
		Assert.assertEquals(ra.timeBooked(di.getTime()), 100);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 300);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 50);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 150);

		group.bookEffort(200, di.getTime(), rt);
		Assert.assertEquals(ra.timeBooked(di.getTime()), 150);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 450);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 0);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 0);

		// over booking ignored
		group.bookEffort(200, di.getTime(), rt);
		Assert.assertEquals(ra.timeBooked(di.getTime()), 150);
		Assert.assertEquals(rb.timeBooked(di.getTime()), 450);
		Assert.assertEquals(ra.effortAvailable(di.getTime(), 1), 0);
		Assert.assertEquals(rb.effortAvailable(di.getTime(), 1), 0);
		Assert.assertEquals(group.timeBooked(di.getTime()), 600);
	}

	public void groupContains() throws PersistenceReferenceException {
		IndividualResource r1 = createIndividualResource("dsowerby");
		IndividualResource r2 = createIndividualResource("mpaine");
		ResourceGroup rg1 = createEmptyResourceGroup();
		rg1.addResource(r1);
		// rg1.save();
		Assert.assertTrue(rg1.contains(r1));
		Assert.assertFalse(rg1.contains(r2));
		Assert.assertTrue(rg1.containsAll(Lists.newArrayList(new IndividualResource[] { r1 })));
		rg1.addResource(r2);
		Assert.assertTrue(rg1.containsAll(Lists.newArrayList(new IndividualResource[] { r1 })));
		Assert.assertTrue(rg1.containsAll(Lists.newArrayList(new IndividualResource[] { r1, r2 })));
		Assert.assertFalse(rg1.containsAllAndOnly(Lists.newArrayList(new IndividualResource[] { r1 })));
		Assert.assertTrue(rg1.containsAllAndOnly(Lists.newArrayList(new IndividualResource[] { r1, r2 })));
		Assert.assertFalse(rg1.containsAllAndOnly(Lists.newArrayList(new IndividualResource[] {})));
		IndividualResource ra1 = createIndividualResource("dsowerby");
		IndividualResource ra2 = createIndividualResource("mpaine");
		ResourceGroup rga1 = new ResourceGroup(connection);
		rga1.addResource(ra1);
		rga1.addResource(ra2);
		Assert.assertTrue(rga1.containsAll(new String[] {}));
		Assert.assertTrue(rga1.containsAll(new String[] { "dsowerby" }));
		Assert.assertTrue(rga1.containsAll(new String[] { "dsowerby", "mpaine" }));
		Assert.assertTrue(rga1.containsAllAndOnly(new String[] { "dsowerby", "mpaine" }));
		Assert.assertFalse(rga1.containsAll(Lists.newArrayList(new IndividualResource[] { r1 })));

	}
}
